package com.sann.demo.util;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;

import java.sql.SQLException;

/**
 * @Aspect 注解表明这是一个切面类
 */
@Component("txManager")
@Aspect
public class TransactionManager {

    /** 用于获取跟当前线程相关的 Connection对象 */
    @Autowired
    private ConnectionUtil connectionUtil;

    @Pointcut("execution(* com.sann.demo.service.impl.*.*(..))")
    public void pc1(){}

    /** 开启事务 */
//    @Before("pc1()")
    public void beginTransaction(){
        try {
            // 关闭自动提交事务，改用手动提交
            connectionUtil.getThreadConnection().setAutoCommit(false);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /** 提交事务 */
//    @AfterReturning("pc1()")
    public void commit(){
        try {
            connectionUtil.getThreadConnection().commit();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /** 回滚事务 */
//    @AfterThrowing("pc1()")
    public void rollback(){
        try {
            connectionUtil.getThreadConnection().rollback();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /** 释放连接,解绑Thread 上的Connection连接 */
//    @After("pc1()")
    public void release(){
        try {
            connectionUtil.getThreadConnection().close();//还回连接池中
            connectionUtil.removeConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }


    /**
     * 在某个版本的AOP注解实现中，最终通知提前与后置通知执行，导致连接关闭，还未提交事务，因此最保险的做法就是通过环绕通知，
     * 内部明确指定事务流程的方式来控制事务。（当前案例是没有发现该顺序问题的）
     *
     * 定义环绕通知来控制事务
     */
    @Around("pc1()")
    public Object around(ProceedingJoinPoint pjp){
        Object result = null;
        // 获取入参
        Object[] args = pjp.getArgs();
        try {
            // 开启事务
            this.beginTransaction();
            result = pjp.proceed(args);
            // 提交事务
            this.commit();
            return result;
        } catch (Throwable throwable) {
            // 回滚事务
            this.rollback();
            throwable.printStackTrace();
            throw new RuntimeException();
        }finally {
            // 释放连接
            this.release();
        }
    }

}
